import { Buffer } from 'node:buffer'
import crypto from 'node:crypto'

/**
 * 过期时间,13位时间戳或数字加m,h,d,分别为多少分钟，多少小时，多少天
 */
type expiresInType = number | `${number}m` | `${number}h` | `${number}d`

export const _jwt = {
  _sign(content: string, secret: string | Buffer) {
    const r = crypto.createHmac('sha256', secret).update(content).digest('base64')
    return this._base64urlEscape(r)
  },
  _base64urlEscapeReg: /[+/=]/g,
  _base64urlEscape(str: string) {
    return str.replace(this._base64urlEscapeReg, (match) => {
      if (match === '+') return '-'
      if (match === '/') return '_'
      if (match === '=') return ''
      return match
    })
  },
  _toBase64(content: Record<string, any>) {
    return this._base64urlEscape(Buffer.from(JSON.stringify(content)).toString('base64'))
  },
  _base64urlUnescapeReg: /[-_]/g,
  _base64urlUnescape(str: string) {
    const paddingLength = (4 - str.length % 4) % 4
    str += '='.repeat(paddingLength)
    return str.replace(this._base64urlUnescapeReg, (match) => {
      if (match === '-') return '+'
      if (match === '_') return '/'
      return match
    })
  },
  sign(data: Record<string, any>, secret: any, option: { expiresIn: expiresInType }) {
    const expiresIn = option.expiresIn || 0
    let timeout = 0
    if (typeof expiresIn === 'number') {
      timeout = expiresIn
    }
    else if (typeof expiresIn === 'string') {
      if (expiresIn.endsWith('m')) {
        timeout = Number.parseInt(expiresIn) * 60 * 1000
      }
      else if (expiresIn.endsWith('h')) {
        timeout = Number.parseInt(expiresIn) * 60 * 60 * 1000
      }
      else if (expiresIn.endsWith('d')) {
        timeout = Number.parseInt(expiresIn) * 24 * 60 * 60 * 1000
      }
    }
    const header = this._toBase64({ typ: 'JWT', alg: 'HS256' })
    data.iat = Date.now()
    data.exp = data.iat + timeout
    const content = this._toBase64(data)
    const sign = this._sign([header, content].join('.'), secret)
    return [header, content, sign].join('.')
  },
  decode(token: string, secret: string | Buffer) {
    const parts = token.split('.')
    if (parts.length !== 3) {
      throw new Error('jwt格式错误')
    }

    const [header, content, sign] = parts
    const newSign = this._sign([header, content].join('.'), secret)

    if (sign !== newSign) {
      throw new Error('jwt被篡改')
    }

    const payload = JSON.parse(Buffer.from(this._base64urlUnescape(content), 'base64').toString())
    const time = Date.now()
    if (time > payload.exp) {
      throw new Error('jwt已过期')
    }
    return payload
  },
  verify(token: any, secret: any) {
    return this.decode(token, secret)
  },
}

export default {
  /**
   * @description 生成token
   * @param {object} data 用户不敏感数据
   * @param {string} secret 密钥
   * @param {object} options 配置项
   * @param {number} options.expiresIn 过期时间,13位时间戳或数字加m,h,d,分别为多少分钟，多少小时，多少天
   */
  sign(data: Record<string, any>, secret: string, option: { expiresIn: expiresInType }) {
    return _jwt.sign(data, secret, option)
  },
  /**
   * 验证token
   * @param {string} Authorization token
   * @param {string} secret 密钥
   */
  verify(Authorization: string, secret: string) {
    if (Authorization.startsWith('Bearer')) {
      Authorization = Authorization.split(' ')[1]
    }
    return _jwt.verify(Authorization, secret)
  },
}

// console.log(_jwt.sign({ name: 'zzh', id: '666zzh' }, 'zzhtest', { expiresIn: 55 }))
// console.log(_jwt.decode('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi6Ien5b-X6bi_IiwiaWQiOiI2NjZ6emgiLCJpYXQiOjE2OTY4MzY0MTQsImV4cCI6MTY5NjgzNjQ2OX0.t3YnXw2-CnHmKDfB333hhrNxh8taCk-4wJcdy-PcmXM', 'zzhtest'))
